VUE.JS 双向绑定实现原理Object.defineProperty()
目前常见的几种 mvc (mvvm)框架 都实现了单向数据绑定。
双向数据绑定就是在单向绑定的基础上给可输入的元素添加change 事件,来动态的修改 model 和 view。
发布者-订阅者模式 (backbone.js)
脏值检查 (angular.js)
数据劫持(vue.js)
发布者-订阅者模式:一般通过 sub,pub 的方式实现数据和视图的监听绑定,更新数据方式通常做法是vm.set('property',value)
这种方式现在显得 low,我们希望通过 vm.property = value
这种方式更新数据,同时更新视图,于是有了下面两种方式
脏值检查:angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过 setInterval()
定时轮询检测数据变动,当然Google不会这么low,angular只有在指定的事件触发时进入脏值检测,大致如下:
- DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
- XHR响应事件 ( $http )
- 浏览器Location变更事件 ( $location )
- Timer事件( $timeout , $interval )
- 执行 $digest() 或 $apply()
数据劫持 : vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。
要实现 mvvm 的双向绑定,必须
- 实现一个数据监听器
Observer
,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 - 实现一个指令解析器
Compile
,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 - 实现一个
Watcher
,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图 - mvvm入口函数,整合以上三者
实现
1. 实现一个 Observer
Observer是一个数据监听器,其实现核心方法就是前文所说的Object.defineProperty( )。如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )处理。如下代码,实现了一个Observer。
1 | function defineReactive(data, key, val) { |
思路分析中,需要创建一个可以容纳订阅者的消息订阅器Dep,订阅器Dep主要负责收集订阅者,然后再属性变化的时候执行对应订阅者的更新函数。所以显然订阅器需要有一个容器,这个容器就是list,将上面的Observer稍微改造下,植入消息订阅器:
1 | //link 这边不太明白 |
从代码上看,我们将订阅器Dep添加一个订阅者设计在getter里面,这是为了让Watcher初始化进行触发,因此需要判断是否要添加订阅者,至于具体设计方案,下文会详细说明的。在setter函数里面,如果数据变化,就会去通知所有订阅者,订阅者们就会去执行对应的更新的函数。到此为止,一个比较完整Observer已经实现了,接下来我们开始设计Watcher。
之后有点不明白
学习链接 vue数据双向绑定原理
2.实现 watcher
1 | //数据描述 |
1 | //存取器描述 |
在ie8下只能在DOM对象上使用,尝试在原生的对象使用 Object.defineProperty()会报错。
1 | <!DOCTYPE html> |